package com.sun.jdk8.parallel;

import org.junit.Test;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Vector;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

/**
 * 在多线程的处理下，肯定会出现数据安全问题。如下：
 */
public class ConcurrentProblem {

    /**
     * 普通方式向集合中添加1000条数据，没有并发问题
     */
    @Test
    public void test01(){
        List<Integer> list = new ArrayList<>();
        for(int i=0;i<1000;i++){
            list.add(i);
        }
        System.out.println(list.size());
    }

    /**
     * 使用并行流向集合中添加1000条数据，存在并发问题
     */
    @Test
    public void test02(){
        List<Integer> list = new ArrayList<>();
        for(int i=0;i<1000;i++){
            list.add(i);
        }
        System.out.println(list.size());
        
        List<Integer> newList = new ArrayList<>();
        //使用并行流向集合中添加1000条数据，存在并发问题
        list.parallelStream().forEach((var)->{newList.add(var);}); //  执行结果要么不是1000，要么会出现并发问题，报错 
        System.out.println(newList.size());
    }

    /**
     * 针对这个问题，我们的解决方案有哪些呢？
     * 1. 加同步锁
     * 2. 使用线程安全的容器
     * 3. 通过Stream中的toArray/collect操作
     */
    
    // 1. 加同步锁
    @Test
    public void test03() {
        List<Integer> listNew = new ArrayList<>();
        Object object = new Object();
        // 在并行流操作时，添加一个同步锁
        IntStream.rangeClosed(1, 1000)
                .parallel()
                .forEach(i -> {
                    synchronized (object) {
                        listNew.add(i);
                    }
                });
        System.out.println(listNew.size());
    }
    
    // 2. 使用线程安全的容器
    @Test
    public void test04() {
        Vector<Integer> vector = new Vector<>();
        IntStream.rangeClosed(1, 1000)
                .parallel()
                .forEach(i -> {
                    vector.add(i);
                });
        System.out.println(vector.size());
    }
    
    // 3. 将线程不安全的容器转换为线程安全的容器
    @Test
    public void test05() {
        List<Integer> listNew = new ArrayList<>();
        List<Integer> synchronizedList = Collections.synchronizedList(listNew);
        IntStream.rangeClosed(1, 1000)
                .parallel()
                .forEach(i -> {
                    synchronizedList.add(i);
                });
        System.out.println(synchronizedList.size());
    }
    
    // 4. 可以通过Stream中的toArray方法或者collect方法来操作，这种做法就是为了满足线程安全问题
    @Test
    public void test06(){
        List<Integer> resultList = IntStream.rangeClosed(1, 1000)
                .parallel()
                .boxed()
                .collect(Collectors.toList());
        System.out.println(resultList.size());
    }
}
